# topicparser.pl
# vi: et sw=2
#
# Topic parser
# Copyright (C) 2009  Harry Karvonen
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
# Author: Harry Karvonen <harry_karvonen@gmail.com>
#

# TODO
# -XDCC integration possibility
#
# ### CHANGELOG
#
# v0.8
#
# Usability improved
# show command fixed
# renamed functions
#
# v0.7
#
# New command parsing system
#
# v0.6
#
# -msgbox layout change
# Ability to remove parser
# New parsing system
# Removed "/$cmd -msgbox" no releases box. 
#
# v0.5
#
# Changelog started
# Fix automatic topic hooking issue

use strict;
use POSIX qw(strftime);

my $channel_file = Xchat::get_info("xchatdir") . "/topicparser.channel";
my $parser_file = Xchat::get_info("xchatdir") . "/topicparser.parser";
my $log_file = Xchat::get_info("xchatdir") . "/topicparser.log";

my $debug = 0;

# We need better command name
my $cmd = "topicparser";

# delay 5 ms. 1 ms is okay but 5 we are sure that topic is updated
my $delay = 5;
my $silent_mode = 0;
my $version = "0.8";
my @timer_hooks;
my @timer_channels;
my %muisti;
my %lastchange;
my %parsers;

Xchat::register("Topic parser", $version,
	"Parse channels topic");

Xchat::hook_command($cmd, "parse_command");

Xchat::hook_server("TOPIC", "topic_hook");

# Parse commands
sub parse_command {
  my $command = $_[0][1];

  if ($command =~ m/^update/i) {
    &update_releases(lc Xchat::get_info("channel"));
  } elsif ($command =~ m/^add/i) {
    &add_parser($_[1][2]);
  } elsif ($command =~ m/^remove/i) {
    &remove_release($_[0][2]);
  } elsif ($command =~ m/^show/i) {
    if ($_[0][2] eq "parser") {
      if ($_[0][3] ne "") {
	&show_parser(lc $_[0][3]);
      } else {
	&show_parser(lc Xchat::get_info("channel"));
      }
    } elsif ($_[0][2] eq "parsers") {
      &show_parser("all");
    } else {
      if ($_[0][2] eq "all") {
	&show_release("all");
      } elsif ($_[0][2] ne "") {
	&show_release(lc $_[0][2]);
      } else {
	&show_release(lc Xchat::get_info("channel"));
      }
    }
  } elsif ($command =~ m/^save/i) {
    &save_releases;
    Xchat::print("Releases saved"); 
  } elsif ($command =~ m/^load/i) {
    &load_releases;
    Xchat::print("Releases loaded"); 
  } elsif ($command =~ m/^silent/i) {
    $silent_mode = $_[0][2];
  } elsif ($command =~ m/^debug/i) {
    if ($_[0][2] ne "") {
      $debug = $_[0][2];
    }

    Xchat::print("Debug level: $debug");
  } elsif ($command =~ m/^help/i) {
    &help;
  } else { 
    &releases($_[0][1]);
  }

  return Xchat::EAT_ALL;
}

# HELP
sub help {
  Xchat::print("Commands:");
  Xchat::print("command only updates current channel");
  Xchat::print("add <parser>");
  Xchat::print("update");
  Xchat::print("show [channel or current channel]");
  Xchat::print("show all (show all channels' releases)");
  Xchat::print("show parser [channel or current channel]");
  Xchat::print("show parsers (list all)");
  Xchat::print("remove [channel or current channel]");
  Xchat::print("save | load");
  Xchat::print("debug <level>");
  Xchat::print("silent <0=off|1=on>");
}

# We need timer because XChat doesn't update get_info in no time
sub timer {
  my $channel = pop(@timer_channels);
  my $timer = pop(@timer_hooks);

  Xchat::set_context($channel);

  # debug
  if ($debug > 1) {
    Xchat::print("timer: $channel topic updating");
  }

  &releases("-msgbox");

  Xchat::unhook($timer);
}

# This function hook timer which hook $cmd
sub topic_hook {
  my $find = 0;
  my $chan = lc Xchat::get_info("channel");

  if ($debug >= 2) {
    Xchat::print("topic_hook: $chan topic change");
  }

  foreach (keys %parsers) {
    if ($_ eq $chan) {
      $find = 1;
    }
  }

  if ($find != 0) {
    if ($debug >= 2) {
      Xchat::print("topic_hook: Updating $chan");
    }
    # Hook timer and storage it to @timer_hooks and channel @timer_channels
    @timer_hooks = (Xchat::hook_timer($delay, "timer"), @timer_hooks);
    @timer_channels =  ($chan, @timer_channels);
  }

  Xchat::EAT_NONE;
}

# Load releases
sub load_releases {
  if (!open(CHANSAVE, "<$channel_file")) {
    Xchat::print("load: Unable open file $channel_file");
  } else {
    %muisti = ();

    foreach (<CHANSAVE>) {
      if ($_ =~ m/^(\S+) (\S+): (.+)$/) {
        $muisti{lc $2} = $3;
	$lastchange{lc $2} = $1;
      }
    }
  }

  close(CHANSAVE);

  
  if (!open(PARSAVE, "<$parser_file")) {
    Xchat::print("load: Unable open file $parser_file");
  } else {
    %parsers = ();

    foreach (<PARSAVE>) {
      if ($_ =~ m/^(\S+): (.+)$/) {
        $parsers{lc $1} = $2;
      }
    }
  }

  close(PARSAVE);

  return Xchat::EAT_XCHAT;
}

# Save releases
sub save_releases {
  if (!open(CHANSAVE, ">$channel_file")) {
    Xchat::print("Unable open file $channel_file");
  } else {
    while ((my $channel, my $releases) = each(%muisti)) {
      print CHANSAVE $lastchange{$channel} . " $channel: $releases\n";
    }
  }

  close CHANSAVE;

  if (!open(PARSAVE, ">$parser_file")) {
    Xchat::print("Unable open file $parser_file");
  } else {
    while ((my $channel, my $parser) = each(%parsers)) {
      print PARSAVE "$channel: $parser\n";
    }
  }

  close PARSAVE;
  return Xchat::EAT_XCHAT;
}

sub log_release {
  if (open(LOGFILE, ">>$log_file")) {
    my $timestamp = strftime "[%x-%H:%M:%S]", gmtime;
    print LOGFILE "$timestamp $_[0]\n";
  } else {
    Xchat::print("ERROR opening $log_file");
  }

  close LOGFILE;
}

sub remove_release {
  my $channel = lc $_[0];

  if ($channel eq "") {
    $channel = lc Xchat::get_info("channel");
  }

  delete $parsers{$channel};
  delete $muisti{$channel};
  delete $lastchange{$channel};

  &save_releases;

  Xchat::print("$channel is removed");

  return Xchat::EAT_XCHAT;
}

# Add parser
sub add_parser {
  $parsers{lc Xchat::get_info("channel")} = $_[0];
  Xchat::print("Channel added parser ${_[0]}");

  return Xchat::EAT_XCHAT;
}

# Update releases
sub update_releases {
  my $channel = $_[0];

  foreach (keys %parsers) {
    if ($debug >= 2) {
      Xchat::print("update: Updating $_");
    }

    Xchat::set_context($_);
    &releases($channel);
  }

  return Xchat::EAT_XCHAT;
}

# Show release
sub show_release {
  if ($_[0] eq "all") {
    while ((my $channel, my $releases) = each(%muisti)) {
      Xchat::print($channel . "\t" . $lastchange{$channel} . " " . $releases);
    }
  } else {
    Xchat::print($_[0] . "\t " . $lastchange{$_[0]} . " " . $muisti{$_[0]});
  }

  return Xchat::EAT_XCHAT;
}

# Show parser
sub show_parser {
  if ($_[0] eq "all") {
    while ((my $channel, my $parser) = each(%parsers)) {
      Xchat::print("$channel\tparser: '" . $parser . "'");
    }
  } else {
    Xchat::print($_[0] . "\tparser: '" . $parsers{$_[0]} . "'");
  }
  
  return Xchat::EAT_XCHAT;
}

# update releases NOTE $_[0] is the channel where msg goes
sub releases {
  # variables
  my $msg_channel = lc $_[0];
  my $channel = lc Xchat::get_info("channel");
  my $topic = Xchat::get_info("topic"); 
  my $release = "";
  my $prefix = "";

  # If we are on other channel??
  if ($msg_channel) {
    $prefix = "$channel:\t";
  } else {
    $msg_channel = $channel;
  }

  # Output
  # Parsing channel topic
  if ($parsers{$channel}) {
    if ($topic =~ m/${parsers{$channel}}/) {
      $release = $1;

      ## mikäli on useampi (.+) tai jotain muuta ( ) settiä
      my @matches = ($topic =~ m/${parsers{$channel}}/);

      for (my $k = 1; $k <= $#matches ; $k++) {
        $release .= " " . $matches[$k];
      }

    } else {
      Xchat::print("${prefix}Couldn't parse! CHECK parse string.", $msg_channel);
    }
  } else {
    Xchat::print("${prefix}parser not found", $msg_channel);
    return Xchat::EAT_XCHAT;
  }

  # Check parsing success
  if ($release) {
    # New channel
    # FIXME tarvitaanko tätä tässä
    #if (!$muisti{$channel}) {
    #  Xchat::print("${prefix}Current release:", $msg_channel);
    #  Xchat::print("${prefix}$release", $msg_channel);
    #  $muisti{$channel} = $release;
    #  $lastchange{$channel} = strftime "[%x-%H:%M:%S]", gmtime;
    # old channel
    #} else {
      if ($muisti{$channel} eq $release) {
	if ($msg_channel eq "-msgbox") {
#	  Xchat::command("GUI MSGBOX \"${prefix}No new release\"");
	} else {
	  Xchat::print("${prefix}No new release", $msg_channel);
	}
      } else {
	if ($msg_channel eq "-msgbox" && $silent_mode != 1) {
	  my $timestamp = strftime "[%x %H:%M:%S]", gmtime;
	  Xchat::command("GUI MSGBOX \"$timestamp $channel new release:\n$release\"");
	} else {
	  Xchat::print("${prefix}New release!", $msg_channel);
	  Xchat::print("${prefix}$release", $msg_channel);
	}

	$muisti{$channel} = $release;
	$lastchange{$channel} = strftime "[%x-%H:%M:%S]", gmtime;
	&log_release("$channel: $release");
      }
    #}
  }

  &save_releases;
  return Xchat::EAT_XCHAT;
}

&load_releases;

if ($debug >= 1) {
  Xchat::print("topicparser: Releases data loaded");
}

Xchat::print("Topicparser v$version");
